using System;
using System.IO;
using System.Windows.Forms;
using Slaks.ClusteringInfo;
using Slaks.DocumentSerialization.Dictionary;
using Slaks.DocumentSerialization.Document;
using Slaks.Graphs;
using Slaks.Graphs.GraphBuilder;
using Slaks.Graphs.GraphBuilder.SchenkerDictionary;
using Slaks.VectorBasics;
using Slaks.Graphs.Algorithms.DistanceAlgorithm;
using Slaks.Graphs.Algorithms;

namespace ClusteringAnalysis.Mediators
{
	public class ResultMediator
	{
		private ListView m_loggingLV;
		private Control m_dunnControl;
		private Control m_dunnSTDControl;
		public ResultMediator(ListView loggingLV,Control dunnControl,Control dunnSTDControl)
		{
			m_loggingLV = loggingLV;
			m_dunnControl = dunnControl;
			m_dunnSTDControl = dunnSTDControl;
		}
		public void Proceed(string dictionaryFile,
			                  string seriesPath,
			                  ListView.ListViewItemCollection clusterFileCollection,
			                  GraphRepresentationType graphRepresentationType,
			                  VectorRepresentationType vectorRepresentationType,
			                  RepresentationClass representationClass,
			                  DistanceType distanceType,
												int distanceValue
			                  )
		{
			try
			{
				//check if the dictionary file exists
				if (File.Exists(dictionaryFile) == false) throw new Exception("Dictionary file is missing or does not exist");
				//check if the series path exists
				if (Directory.Exists(seriesPath) == false) throw new Exception("Series path is missing or does not exist");

				//check if the cluster files are provided
				if (clusterFileCollection.Count <= 0) throw new Exception("Provide at least one cluster file");

				this.ClearLogging();

				//load the dictionary
				DictionaryMap dictionary = DictionaryInfo.ReadXml(dictionaryFile);

				this.AddLogging("Representation class : " + representationClass);
				 if (representationClass == RepresentationClass.Graph)
					 this.AddLogging("Graph-representation type : " + graphRepresentationType);
				 else
					 this.AddLogging("Vector-representation type : " + vectorRepresentationType);
				this.AddLogging("Distance type : " + distanceType);
				this.AddLogging("Dictionary was loaded with " + dictionary.Count + " entries");

				ClusteringInfo clusters;
				double dunnIndex = 0.0;
				double tempDunnIndex;

				double []tempDunnIndices = new double[clusterFileCollection.Count];

				int index = 0;
				foreach(ListViewItem file in clusterFileCollection)
				{
					clusters = this.LoadClusteringInfo(file.Text);
					this.AddLogging("Cluster " + clusters.CurrentIteration + " loaded with " + clusters.TotalSize + " items");
					if (representationClass == RepresentationClass.Graph)
					{
						tempDunnIndex = this.CalculateDunnIndex4Graphs(dictionary,clusters,graphRepresentationType,distanceType,distanceValue,seriesPath);
						dunnIndex += tempDunnIndex;
					}
					else
					{
						tempDunnIndex = this.CalculateDunnIndex4Vectors(dictionary,clusters,vectorRepresentationType,distanceType,seriesPath);
						dunnIndex += tempDunnIndex;
					}

					tempDunnIndices[index++] = tempDunnIndex;
					this.AddLogging("Dunn index : " + tempDunnIndex);
				}
				if (dunnIndex != 0.0) dunnIndex /= clusterFileCollection.Count;

				this.SetDunnIndex(dunnIndex.ToString());
				double dunnSTD = GetStandardDeviation(tempDunnIndices,dunnIndex);
				this.SetDunnSTD(dunnSTD.ToString());
			}
			catch(Exception ex)
			{
				MessageBox.Show(ex.Message, "Mediator Proceed Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		private double CalculateDunnIndex4Graphs(DictionaryMap dictionaryMap,
			                                       ClusteringInfo clusters,
																						 GraphRepresentationType representation,
																						 DistanceType distanceType,
																						 int distanceValue,
																						 string seriesPath)
		{
			double dunnIndex = 0.0;

			GraphDistanceBase distanceMeasure = null;
			if (distanceType == DistanceType.Mcs)
			{
				if (representation == GraphRepresentationType.RawFrequencySchenker ||
					  representation == GraphRepresentationType.NormalizedFrequencySchenker)
					distanceMeasure = new DistMCS4VertexEdgeFrequency();
				else
					distanceMeasure = new DistMCS4VertexEdgeCount ();
			}
			else if (distanceType == DistanceType.Wgu)
			{
				if (representation == GraphRepresentationType.RawFrequencySchenker ||
					  representation == GraphRepresentationType.NormalizedFrequencySchenker)
					distanceMeasure = new DistWGU4VertexEdgeFrequency();
				else
					distanceMeasure = new DistWGU4VertexEdgeCount();
			}
			else
			{
				throw new Exception("Representation type " + representation + " is not valid");
			}

			GraphList [] graphLists = new GraphList[clusters.ClusterNumber];

			int index = 0;
			string documentPath;
			string centroidDocumentPath;
			Document document;
			Graph graph;
			foreach(Cluster cluster in clusters.GetClusters())
			{
				GraphList graphList = new GraphList();
				centroidDocumentPath = cluster.Centroid.DocumentName;
				foreach(DocumentInfo documentInfo in cluster.GetDocuments())
				{
					documentPath = seriesPath + documentInfo.DocumentName;
					document = ParseDocument(documentPath);
					graph = LoadDocumentAsGraph(representation,document,dictionaryMap,distanceValue);
					if (documentInfo.DocumentName == centroidDocumentPath) graphList.SetCentroid(graph);
					graphList.Add(graph);
				}
				graphLists[index++] = graphList;
			}


			dunnIndex = Slaks.Graphs.Analysis.ClusteringAnalysis.GetDunnIndex(graphLists,distanceMeasure);

			return dunnIndex;
		}


		private double CalculateDunnIndex4Vectors(DictionaryMap dictionaryMap,
			ClusteringInfo clusters,
			VectorRepresentationType representation,
			DistanceType distanceType,
			string seriesPath)
		{
			double dunnIndex = 0.0;

			VectorDistanceMeasure distanceMeasure = null;
			if (distanceType == DistanceType.Cos)
			{
				if (representation == VectorRepresentationType.Standard ||
					  representation == VectorRepresentationType.Simple)
					distanceMeasure = new VectorDistanceMeasure(Slaks.VectorBasics.Algorithms.VectorDistances.GetCosinus);
			}
			else if (distanceType == DistanceType.Jacaard)
			{
				if (representation == VectorRepresentationType.Standard ||
					representation == VectorRepresentationType.Simple)
					distanceMeasure = new VectorDistanceMeasure(Slaks.VectorBasics.Algorithms.VectorDistances.GetJacaard);
			}
			else
			{
				throw new Exception("Representation type " + representation + " is not valid");
			}

			VectorList [] vectorLists = new VectorList[clusters.ClusterNumber];

			int index = 0;
			string documentPath;
			string centroidDocumentPath;
			Document document;
			Vector vector;
			foreach(Cluster cluster in clusters.GetClusters())
			{
				VectorList vectorList = new VectorList();
				centroidDocumentPath = cluster.Centroid.DocumentName;
				foreach(DocumentInfo documentInfo in cluster.GetDocuments())
				{
					documentPath = seriesPath + documentInfo.DocumentName;
					document = ParseDocument(documentPath);
					vector = LoadDocumentAsVector(representation,document,dictionaryMap);
					if (documentInfo.DocumentName == centroidDocumentPath) vectorList.SetCentroid(vector);
					vectorList.Add(vector);
				}
				vectorLists[index++] = vectorList;
			}


			dunnIndex = Slaks.VectorBasics.Analysis.ClusteringAnalysis.GetDunnIndex(vectorLists,distanceMeasure);

			return dunnIndex;
		}

		private delegate void ClearLoggingHandler();
		private void ClearLogging()
		{
			if (m_loggingLV.InvokeRequired)
			{
				m_loggingLV.Invoke(new ClearLoggingHandler(this.ClearLogging));
				return;
			}
			m_loggingLV.Items.Clear();
		}
		private delegate void AddLoggingHandler(string message);
		private void AddLogging(string message)
		{
			if (m_loggingLV.InvokeRequired)
			{
				m_loggingLV.Invoke(new AddLoggingHandler(AddLogging),new object[]{message});
				return;
			}
			m_loggingLV.Items.Add(message);
		}

		private delegate void SetDunnIndexHandler(string dunnIndex);
		private void SetDunnIndex(string dunnIndex)
		{
			if (m_dunnControl.InvokeRequired)
			{
				m_dunnControl.Invoke(new SetDunnIndexHandler(SetDunnIndex),new object[]{dunnIndex});
				return;
			}
			m_dunnControl.Text = dunnIndex;
		}
		
		private delegate void SetDunnSTDHandler(string dunnSTD);
		private void SetDunnSTD(string dunnSTD)
		{
			if (m_dunnSTDControl.InvokeRequired)
			{
				m_dunnSTDControl.Invoke(new SetDunnSTDHandler(SetDunnSTD),new object[]{dunnSTD});
				return;
			}
			m_dunnSTDControl.Text = dunnSTD;
		}

		private ClusteringInfo LoadClusteringInfo(string file)
		{
			return ClusteringInfoSerialization.ReadXml(file);
		}

		private Document ParseDocument(string file)
		{
			return Xml2Document.ReadXml(file);
		}
		private Graph LoadDocumentAsGraph(GraphRepresentationType representation,
			Document document, 
			DictionaryMap dictionaryMap,
			int distanceValue)
		{
			Graph graph = null;

			IDictionaryRepresentation dictionaryRepresentation;

				switch(representation)
				{
					case GraphRepresentationType.StandardSchenker :
						dictionaryRepresentation = new StandardDictionaryRepresentationSchenker();
						break;
					case GraphRepresentationType.SimpleSchenker :
						dictionaryRepresentation = new SimpleDictionaryRepresentationSchenker();
						break;
					case GraphRepresentationType.NDistanceSchenker : 
						dictionaryRepresentation = new NDistanceDictionaryRepresentationSchenker();
						break;
					case GraphRepresentationType.NSimpleDistanceSchenker :
						dictionaryRepresentation = new NSimpleDistanceDictionaryRepresentationSchenker();
						break;
					case GraphRepresentationType.NormalizedFrequencySchenker :
						dictionaryRepresentation = new NormalizedFrequencyDictionaryRepresentationSchenker();
						break;
					case GraphRepresentationType.RawFrequencySchenker :
						dictionaryRepresentation = new RawFrequencyDictionaryRepresentationSchenker();
						break;
					default:
						throw new Exception("Representation not supported currently");
				}
			

			try
			{
			
				if (representation != GraphRepresentationType.NDistanceSchenker &&
					representation != GraphRepresentationType.NSimpleDistanceSchenker)
					graph = dictionaryRepresentation.Build(document,dictionaryMap);
				else
					graph = dictionaryRepresentation.Build(document,distanceValue,dictionaryMap);
			}
			catch(GraphException ex)
			{
				MessageBox.Show(ex.Message);
			}
			return graph;
		}

		private Vector LoadDocumentAsVector(VectorRepresentationType representation,Document document, DictionaryMap dictionaryMap)
		{
			Vector vector = null;
			try
			{
				switch(representation)
				{
					case VectorRepresentationType.Standard :
						vector = Slaks.VectorBasics.VectorBuilder.StandardRepresentation.Build(document,dictionaryMap);
						break;
					case VectorRepresentationType.Simple:
						vector = Slaks.VectorBasics.VectorBuilder.SimpleRepresentation.Build(document,dictionaryMap);
						break;
					default:
						throw new Exception("Representation not supported currently");
				}
			}catch(VectorException ex)
			{
			  MessageBox.Show(ex.Message);
			}

			return vector;
		}

		private static double GetStandardDeviation(double[] results, double average)
		{
			double deviation = 0.0;
			for (int i = 0; i < results.Length; i++)
			{
				deviation += ((results[i] - average)*(results[i]*average));
			}

			if (results.Length == 1) return Math.Sqrt(deviation);

			deviation /= (results.Length - 1);

			deviation /= Math.Sqrt(deviation);

			return deviation;
		}
	}
}
